package org.oimwrapper.connectors.common;

import Thor.API.*;
import Thor.API.Operations.*;
import com.thortech.xl.vo.*;
import java.util.*;

import org.oimwrapper.connectors.*;

/**
 * Scans for attestation events assigned to a proxy user and acts upon them 
 * accordingly.<p>
 *
 * This task expects the following parameters, in addition to the standard
 * parameters in the {@link OIMTaskBase} class.
 * <ul>
 * <li><code>CertifyComment</code> The comment to use when certifying a record.</li>
 * <li><code>DelegateComment</code> The comment to use when delagating a record.</li>
 * <li><code>PageSize</code> The number of records to retrieve in each batch.</li>
 * <li><code>ProxyUser</code> The user that the attestation events are presently 
 *                            assigned to.</li>
 * <li><code>RejectComment</code> The comment to use when rejecting a record.</li>
 * <li><code>Threads</code> The number of concurrent threads to run to process 
 *                          records.  Setting this to 1 disables threading.</li>
 * </ul>
 */
public abstract class OIMAttestationRouterBase extends OIMTaskBase {
	
	/**
	 * Indicates that the record in question should be certified.
	 */
	public static final int CertifyResource = 1;
	
	/**
	 * Indicates that the record in question should be rejected.
	 */
	public static final int RejectResource = 2;
	
	/**
	 * Indicates that the record in question should be delegated to another user.
	 */
	public static final int DelegateResource = 3;
	
	/**
	 * The attestation operation used by this class.
	 */
	private AttestationOperationsIntf attestOp;

	/**
	 * True if a stop has been requested for this task.
	 */
	private boolean stop;

	/**
	 * Default constructor.
	 */
	public OIMAttestationRouterBase() {
		super("OIMWRAPPER.TASK");
		stop = false;
	}
	
	/**
	 * Constructor to specify the log category.
	 * 
	 * @param LogCategory The log category to use.
	 */
	public OIMAttestationRouterBase(String LogCategory) {
		super(LogCategory);
		stop = false;
	}

	/**
	 * This method is called by the scheduler to execute the task instance.
	 */
	public final void execute() {
		logger.debug("Entering attestationRouterBase.execute()");
		try {
			attestOp = (AttestationOperationsIntf) getUtility(AttestationOperationsIntf.class);
			long proxyUserKey = getOimUser(getCriticalAttribute("ProxyUser"));
			tcResultSet tasks = attestOp.getPendingAttestationTasks(proxyUserKey);
			int maxWorkers = Integer.parseInt(getDefaultAttribute("Threads", "10"));
			int pageSize = Integer.parseInt(getDefaultAttribute("PageSize", "100"));
			for (int i = 0; i < tasks.getRowCount(); i++) {
				tasks.goToRow(i);
				long requestKey = tasks.getLongValue("Attestation Requests.Key");
				long taskKey = tasks.getLongValue("Attestation Tasks.Key");
				Map searchFor = new HashMap();
				searchFor.put("Attestation Tasks.Key", Long.toString(taskKey));
				for (int j = 1; true; j += pageSize) {
					tcResultSet details = attestOp.getAttestationRequestDetails(requestKey, searchFor, j, pageSize);
					if (details.getRowCount() == 0) {
						break;
					}
					for (int k = 0; k < details.getRowCount(); k++) {
						details.goToRow(k);
						long recordKey = details.getLongValue("Attestation Data.Record Key"); 
						long userKey = details.getLongValue("Provisioned User.Key"); 
						startThread(maxWorkers, new worker(this, taskKey, recordKey, userKey));
					}
				}
				waitForAllThreads();
				attestOp.attestTask(taskKey);
			}
		} catch (Exception e) {
			OIMExceptionHandler.handleException(logger, e, getClass());
		}
		logger.debug("Exiting attestationRouterBase.execute()");
	}
	
	/**
	 * This method is called by the scheduler to stop a running task instance.
	 * 
	 * @return <code>true</code>
	 */
	public final boolean stop() {
		logger.debug("Entering attestationRouterBase.stop()");
		logger.warn(" ---- Stopping current task ---- ");
		stop = true;
		logger.debug("Exiting attestationRouterBase.stop()");
		return stop;
	}
	
	/**
	 * Processes a single attestation record.
	 * 
	 * @param TaskKey The key of the attestation task being processed.
	 * @param RecordKey The key of the record to process.
	 * @param UserKey The key of the user associated with the record.
	 * @exception Exception
	 */
	public final void processSingleEvent(long TaskKey, long RecordKey, long UserKey) throws Exception {
		logger.debug("Entering attestationRouterBase.processSingleEvent()");
		OIMFormData data = new OIMFormData(attestOp.getEntitlementRecordDetails(RecordKey));
		AttestationItemResponse response = new AttestationItemResponse();
		response.setRecordKey(RecordKey);
		switch (getActionForRecord(UserKey, data)) {
			case CertifyResource:
				response.setResponse("Certify");
				response.setComment(getDefaultAttribute("CertifyComment", ""));
				logger.debug("Certifying " + Long.toString(RecordKey));
				break;
			case RejectResource:
				response.setResponse("Reject");
				response.setComment(getDefaultAttribute("RejectComment", ""));
				logger.debug("Rejecting " + Long.toString(RecordKey));
				break;
			case DelegateResource:
				String delegate = getDelegateUser(UserKey, data).toUpperCase();
				response.setResponse("Delegate");
				response.setDelegateTo(delegate);
				response.setComment(getDefaultAttribute("DelegateComment", ""));
				logger.debug("Delegating " + Long.toString(RecordKey) + " to " + delegate);
				break;
		}
		attestOp.submitReponses(TaskKey, new AttestationItemResponse[] {response}, "", "");
		logger.debug("Exiting attestationRouterBase.processSingleEvent()");
	}
	
	/**
	 * Indicates what action should be taken for a given resource.
	 * 
	 * @param TargetUserKey The key of the user that owns the resource.
	 * @param FormData The form data associated with the resource.
	 * @return {@link OIMAttestationRouterBase#CertifyResource} to certify the resource, 
	 *         {@link OIMAttestationRouterBase#RejectResource} to reject it, or 
	 *         {@link OIMAttestationRouterBase#DelegateResource} to delegate to another 
	 *         user.
	 * @exception Exception
	 */
	public abstract int getActionForRecord(long TargetUserKey, OIMFormData FormData) throws Exception;
	
	/**
	 * Gets the username of the user that the record should be delegated to.
	 * 
	 * @param TargetUserKey The key of the user that owns the resource.
	 * @param FormData The form data associated with the resource.
	 * @return The username of the user to delegate the record to.
	 * @exception Exception
	 */
	public abstract String getDelegateUser(long TargetUserKey, OIMFormData FormData) throws Exception;
	
	/**
	 * A worker thread to process one event.
	 */
	private class worker extends OIMTaskThreadBase {
		
		/**
		 * The calling task.
		 */
		private OIMAttestationRouterBase task;
		
		/**
		 * The key of the attestation task being processed.
		 */
		private long taskKey;
		
		/**
		 * The key of the record.
		 */
		private long recordKey;
		
		/**
		 * The key of the user.
		 */
		private long userKey;
		
		/**
		 * Default constructor
		 * 
		 * @param Task The calling task.
		 * @param TaskKey The key of the attestation task being processed.
		 * @param RecordKey The key of the record.
		 * @param UserKey The key of the user.
		 */
		private worker(OIMAttestationRouterBase Task, long TaskKey, long RecordKey, long UserKey) {
			super(Task);
			task = Task;
			taskKey = TaskKey;
			recordKey = RecordKey;
			userKey = UserKey;
		}
		
		/**
		 * Processes a single event.
		 * 
		 * @exception Exception
		 */
		public void runOimThread() throws Exception {
			task.processSingleEvent(taskKey, recordKey, userKey);
		}
	}
}